#include <iostream>
#include <QtOpenGL>

#include "src/light.h"
#include "src/material.h"
#include "glwidget.h"
#include "src/camera.h"

GLWidget::GLWidget(QWidget* _parent) : QGLWidget(_parent){
	xMov = 0;
	yMov = 0;
	zMov = 0;
	xRot = 0;
	yRot = 0;
	zRot = 0;
	zoom = 2.0;

    showAxis = true;
    showGrid = true;
    showRayTest = false;
    showCamera = false;
    showWireframeObject = false;
    changeCamera = false;
//
//	light0_pos[0] = 5.0;
//	light0_pos[1] = -10.0;
//	light0_pos[2] = 10.0;
//	light0_pos[3] = 0.0;
//
//	light0_ambient[0] = 0.1;
//	light0_ambient[1] = 0.1;
//	light0_ambient[2] = 0.1;
//	light0_ambient[3] = 1.0;
//
//	light0_diffuse[0] = 0.8;
//	light0_diffuse[1] = 0.8;
//	light0_diffuse[2] = 0.8;
//	light0_diffuse[3] = 1.0;
//
//	light0_specular[0] = 0.3;
//	light0_specular[1] = 0.3;
//	light0_specular[2] = 0.3;
//	light0_specular[3] = 1.0;
//
//	global_ambient[0] = 0.5;
//	global_ambient[1] = 0.5;
//	global_ambient[2] = 0.5;
//	global_ambient[3] = 1.0;

    scene = NULL;
    scene = newScene();
}

GLWidget::~GLWidget() {
    delete scene;
}

Scene *GLWidget::newScene(){
    if(scene) delete scene;
    scene = new Scene();
	updateGL();
	return scene;
}

Scene *GLWidget::getScene(){
    return scene;
}

bool GLWidget::isShowingAxis() const{
	return showAxis;
}

bool GLWidget::isShowingGrid() const{
	return showGrid;
}

bool GLWidget::isShowingRayTest() const{
	return showRayTest;
}

bool GLWidget::isShowingCamera() const{
	return showCamera;
}

bool GLWidget::isChangingCamera() const{
	return changeCamera;
}

void GLWidget::changeShowAxis(bool _b){
	showAxis = _b;
	updateGL();
}

void GLWidget::changeShowGrid(bool _b){
	showGrid = _b;
	updateGL();
}

void GLWidget::changeShowRayTest(bool _b){
	showRayTest = _b;
	updateGL();
}

void GLWidget::changeShowCamera(bool _b){
	showCamera = _b;
	updateGL();
}

void GLWidget::changeChangeCamera(bool _b){
	changeCamera = _b;
	updateGL();
}

void GLWidget::setXRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != xRot) {
		xRot = _angle;
		updateGL();
	}
}

void GLWidget::setYRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != yRot) {
		yRot = _angle;
		updateGL();
	}
}

void GLWidget::setZRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != zRot) {
		zRot = _angle;
		updateGL();
	}
}

void GLWidget::changeObjectView(bool _b){
    showWireframeObject = _b;

    updateGL();
}

void GLWidget::initializeGL(){
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_FLAT);
	glEnable(GL_CULL_FACE);

    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);

    glEnable(GL_BLEND);
//    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    Light *light = scene->getLight();
    Material material = light->getMaterial();
    //luz
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light->getGlobalGL());

    GLfloat pos[4];
    pos[0] = light->getPosition().x;
    pos[1] = light->getPosition().y;
    pos[2] = light->getPosition().z;
    pos[3] = 1;

    glLightfv(GL_LIGHT0, GL_POSITION, pos);
    glLightfv(GL_LIGHT0, GL_AMBIENT, material.getAmbientGL());
    glLightfv(GL_LIGHT0, GL_DIFFUSE, material.getDiffuseGL());
    glLightfv(GL_LIGHT0, GL_SPECULAR, material.getSpecularGL());
//
//
//    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
//    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
//
//    glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);
//    glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
//    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light0_diffuse);
//    glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);

}

void GLWidget::paintGL(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

    glPushMatrix();

	glTranslated(xMov, yMov, zMov);

    //rotacao
	glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
	glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
	glRotated(zRot / 16.0, 0.0, 0.0, 1.0);


    if (scene) scene->draw(showWireframeObject, showCamera, showRayTest);


    if(showAxis){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);
        //eixos
        glLineWidth(2);

        glBegin(GL_LINES);
            glColor3f(1.0, 0.0, 0.0);
            //eixo X
            glVertex3f( 0, 0, 0);
            glVertex3f( 1, 0, 0);

            glColor3f(0.0, 1.0, 0.0);
            //eixo y
            glVertex3f( 0, 0, 0);
            glVertex3f( 0, 1, 0);

            glColor3f(0.0, 0.0, 1.0);
            //eixo z
            glVertex3f( 0, 0, 0);
            glVertex3f( 0, 0, 1);
        glEnd();
        glLineWidth(1);

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);
    }
    if(showGrid){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);

        int square = 20;
        glColor4f(0.5, 0.5, 0.5, 0.5);
        glBegin(GL_LINES);
        for(int i = -square; i <= square; i++){
            glVertex3f( i, square, 0);
            glVertex3f( i, -square, 0);

            glVertex3f( square, i, 0);
            glVertex3f( -square, i, 0);
        }
        glEnd();

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

    }

    glColor3f(1.0, 1.0, 1.0);
    glPopMatrix();
}

void GLWidget::resizeGL(int _width, int _height) {
	glViewport(0, 0, _width, _height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	double f = 0.30*zoom;
	if (_width<=_height)
		glFrustum(-f,     f,    -f*((double)_height)/_width,    f*((double)_height)/_width,     1, 50.0);
	else
		glFrustum(-f*((double)_width)/_height,  f*((double)_width)/_height,     -f,     f,    1, 50.0);

	gluLookAt(0.0, -10, 10.0,   0.0, 0.0, 0.0,  0.0, 0.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
}

QSize GLWidget::minimumSizeHint() const {
	return QSize(50, 50);
}

QSize GLWidget::sizeHint() const {
	return QSize(400, 400);
}

void GLWidget::wheelEvent(QWheelEvent *_event){
    if(_event->delta() > 0){
        if (zoom > 1){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom -= 2.0;
            else
                zoom -= 0.1;
        }else if(zoom > 0.1){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom -= 0.5;
            else
                zoom -= 0.01;
        }
    }
    else{
        if (zoom < 100.0){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom += 2.0;
            else
                zoom += 0.1;
        }
    }
    resizeGL(width(), height());
    updateGL();
}

void GLWidget::mousePressEvent(QMouseEvent *_event){
	lastPos = _event->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *_event){
	int dx = _event->x() - lastPos.x();
	int dy = _event->y() - lastPos.y();

	if (_event->buttons() & Qt::LeftButton) {
        if (_event->modifiers() & Qt::ShiftModifier) {
            if ( fabs(lastPos.x() - _event->x()) > 3 ){
                int dx = -1;
                if(lastPos.x() - _event->x() < 0.0) dx = 1;

                xMov += 0.5 * dx;
                updateGL();
            }
            if ( fabs(lastPos.y() - _event->y()) > 3 ){
                int dy = 1;
                if(lastPos.y() - _event->y() < 0.0) dy = -1;

                yMov += 0.5*dy;
                updateGL();
            }

		} else {
            setXRotation(xRot + 8 * dy);
            setYRotation(yRot + 8 * dx);
		}
	} else if (_event->buttons() & Qt::RightButton) {
        if (_event->modifiers() & Qt::ShiftModifier) {
            if ( fabs(lastPos.x() - _event->x()) > 3 ){
                int dx = -1;
                if(lastPos.x() - _event->x() < 0.0) dx = 1;

                xMov += 0.5 * dx;
                updateGL();
            }
            if ( fabs(lastPos.y() - _event->y()) > 3 ){
                int dz = 1;
                if(lastPos.y() - _event->y() < 0.0) dz = -1;

                zMov += 0.5*dz;
                updateGL();
            }

		} else {
            setXRotation(xRot + 8 * dy);
            setZRotation(zRot + 8 * dx);
		}
	}

    lastPos = _event->pos();
}

void GLWidget::normalizeAngle(int *_angle){
	while (*_angle < 0)
		*_angle += 360 * 16;
	while (*_angle > 360 * 16)
		*_angle -= 360 * 16;
}
